namespace OasisCiq.Xml
{
    using System;
    using System.IO;
    using System.Xml;
    using Xal;
    using Xnal;
    using Xnl;
    using Xpil;
    using XS;

    public class CiqXmlValidatingReader : XmlReader
    {
        private static readonly XmlReaderSettings SharedSettings;
        private readonly XmlReader wrapper;
        private readonly XnlXmlReader xnlReader;
        private readonly XalXmlReader xalReader;
        private readonly XnalXmlReader xnalReader;
        private readonly XpilXmlReader xpilReader;

        static CiqXmlValidatingReader()
        {
            SharedSettings = new XmlReaderSettings
            {
                CloseInput = false,
                IgnoreWhitespace = true,
                ValidationType = ValidationType.Schema
            };

            LoadSchemas();
        }

        public CiqXmlValidatingReader(XmlReader reader)
        {
            // TODO: Merge settings
            wrapper = Create(reader, SharedSettings);
            xnlReader = new XnlXmlReader(wrapper);
            xalReader = new XalXmlReader(wrapper);
            xnalReader = new XnalXmlReader(wrapper);
            xpilReader = new XpilXmlReader(wrapper);
        }

        public override int AttributeCount
        {
            get { return wrapper.AttributeCount; }
        }

        public override string BaseURI
        {
            get { return wrapper.BaseURI; }
        }

        public override int Depth
        {
            get { return wrapper.Depth; }
        }

        public override bool EOF
        {
            get { return wrapper.EOF; }
        }

        public override bool HasValue
        {
            get { return wrapper.HasValue; }
        }

        public override bool IsEmptyElement
        {
            get { return wrapper.IsEmptyElement; }
        }

        public override string LocalName
        {
            get { return wrapper.LocalName; }
        }

        public override XmlNameTable NameTable
        {
            get { return wrapper.NameTable; }
        }

        public override string NamespaceURI
        {
            get { return wrapper.NamespaceURI; }
        }

        public override XmlNodeType NodeType
        {
            get { return wrapper.NodeType; }
        }

        public override string Prefix
        {
            get { return wrapper.Prefix; }
        }

        public override ReadState ReadState
        {
            get { return wrapper.ReadState; }
        }

        public override XmlReaderSettings Settings
        {
            get { return wrapper.Settings;  }
        }

        public override string Value
        {
            get { return wrapper.Value; }
        }

        public NameLine ReadNameLine()
        {
            return xnlReader.ReadNameLine();
        }

        public PersonNameType ReadPersonName()
        {
            return xnlReader.ReadPersonName();
        }

        public OrganisationNameType ReadOrganisationName()
        {
            return xnlReader.ReadOrganisationName();
        }

        public AddressType ReadAddress()
        {
            return xalReader.ReadAddress();
        }

        public PostalLabel ReadPostalLabel()
        {
            return xnalReader.ReadPostalLabel();
        }

        public Record ReadRecord()
        {
            return xnalReader.ReadRecord();
        }

        public OrganisationDetailsType ReadOrganisationDetails()
        {
            return xpilReader.ReadOrganisationDetails();
        }

        public PartyType ReadXpilParty()
        {
            return xpilReader.ReadParty();
        }

        public PersonDetailsType ReadPersonDetails()
        {
            return xpilReader.ReadPersonDetails();
        }

        public override void Close()
        {
            wrapper.Close();
        }

        public override string GetAttribute(int i)
        {
            return wrapper.GetAttribute(i);
        }

        public override string GetAttribute(string name, string namespaceUri)
        {
            return wrapper.GetAttribute(name, namespaceUri);
        }

        public override string GetAttribute(string name)
        {
            return wrapper.GetAttribute(name);
        }

        public override string LookupNamespace(string prefix)
        {
            return wrapper.LookupNamespace(prefix);
        }

        public override bool MoveToAttribute(string name, string ns)
        {
            return wrapper.MoveToAttribute(name, ns);
        }

        public override bool MoveToAttribute(string name)
        {
            return wrapper.MoveToAttribute(name);
        }

        public override bool MoveToElement()
        {
            return wrapper.MoveToElement();
        }

        public override bool MoveToFirstAttribute()
        {
            return wrapper.MoveToFirstAttribute();
        }

        public override bool MoveToNextAttribute()
        {
            return wrapper.MoveToNextAttribute();
        }

        public override bool Read()
        {
            return wrapper.Read();
        }

        public override bool ReadAttributeValue()
        {
            return wrapper.ReadAttributeValue();
        }

        public override void ResolveEntity()
        {
            wrapper.ResolveEntity();
        }

        private static void LoadSchemas()
        {
            const string coreResourcePrefix = "OasisCiq.Schemas.";
            const string customisableResourcePrefix = "OasisCiq.Schemas.";

            const string commonTypesPath = coreResourcePrefix + "CommonTypes.xsd";
            const string xalPath = coreResourcePrefix + "xAL.xsd";
            const string xlinkPath = coreResourcePrefix + "xlink-2003-12-31.xsd";
            const string xnalPath = coreResourcePrefix + "xNAL.xsd";
            const string xnlPath = coreResourcePrefix + "xNL.xsd";
            const string xpilPath = coreResourcePrefix + "xPIL.xsd";
            const string xalTypesPath = customisableResourcePrefix + "xAL-types.xsd";
            const string xnalTypesPath = customisableResourcePrefix + "xNAL-types.xsd";
            const string xnlTypesPath = customisableResourcePrefix + "xNL-types.xsd";
            const string xpilTypesPath = customisableResourcePrefix + "xPIL-types.xsd";

            var coreAssembly = typeof(CiqXmlValidatingReader).Assembly;
            var customisableAssembly = typeof(XSString).Assembly;

            var addSchema = new Action<string, Stream>(
            (n, v) =>
            {
                using (var xr = Create(v))
                {
                    SharedSettings.Schemas.Add(n, xr);
                }
            });

            using (var commonTypes = coreAssembly.GetManifestResourceStream(commonTypesPath))
            using (var xal = coreAssembly.GetManifestResourceStream(xalPath))
            using (var xlink = coreAssembly.GetManifestResourceStream(xlinkPath))
            using (var xnal = coreAssembly.GetManifestResourceStream(xnalPath))
            using (var xnl = coreAssembly.GetManifestResourceStream(xnlPath))
            using (var xpil = coreAssembly.GetManifestResourceStream(xpilPath))
            using (var xalTypes = customisableAssembly.GetManifestResourceStream(xalTypesPath))
            using (var xnalTypes = customisableAssembly.GetManifestResourceStream(xnalTypesPath))
            using (var xnlTypes = customisableAssembly.GetManifestResourceStream(xnlTypesPath))
            using (var xpilTypes = customisableAssembly.GetManifestResourceStream(xpilTypesPath))
            {
                addSchema("urn:oasis:names:tc:ciq:ct:3", commonTypes);
                addSchema("urn:oasis:names:tc:ciq:xal:3", xalTypes);
                addSchema("urn:oasis:names:tc:ciq:xal:3", xal);
                addSchema("http://www.w3.org/1999/xlink", xlink);
                addSchema("urn:oasis:names:tc:ciq:xnal:3", xnalTypes);
                addSchema("urn:oasis:names:tc:ciq:xnal:3", xnal);
                addSchema("urn:oasis:names:tc:ciq:xnl:3", xnlTypes);
                addSchema("urn:oasis:names:tc:ciq:xnl:3", xnl);
                addSchema("urn:oasis:names:tc:ciq:xpil:3", xpilTypes);
                addSchema("urn:oasis:names:tc:ciq:xpil:3", xpil);
            }
        }
    }
}